/* pwguesser.c                                               *
  * CS4235 - Programming Assignment #1  *
  * Dustin Shiflett & Johannes Schuett        */

#include <stdio.h>
#include <string.h>
#include <stdlib.h> /*exit()*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h> /*to get return code from server*/
#include <unistd.h> /*will allow my to execute another process*/
#include <time.h> /*difftime, */
#include <sys/time.h> /*gettimeofday, */

#include "pwguesser.h"

int debug = 0;

/*valid input is ./pwguesser (block length) (server executable name) [-d for debug]*/
int main(int argc, char *argv[]) {
    
    int i, n, maxVal2 = 0, maxIndex=-1, blockSize, serverStatus, loginSuccess = 0, errorCode, currChar = 0;
    int currPassIndex = 0;
    /*char sequence[MAX_LENGTH];*/
    FILE* checkFile;
    const char serverArgs[] = " check.pw testPW 100 > /dev/null";
    char currPass[10];
    const char charSet[] = "ACTG";     /*Possible letters in password: A, C, T, G*/
    struct timeval checkTime[4]; /*ulong, because I have no idea how many clockticks may pass*/
    char* serverCmd; /*Command we will execute to test passwords that we are timing*/
    char* charBlock;
    struct timeval startTime;
    struct timeval stopTime;
    /* unsigned long elapsedTime; */
    struct timeval elapsedTime;
    struct timeval maxVal;
    size_t writeSize;
    double t1, t2;
    double checkTime2[4];
    
    /*checking number of command line arguments*/
    if((argc != 3) && (argc != 4)) {
        printf("usage: %s <# of times each character repeats> <Server Executable> [-d]\nServer Executable: e.g. ./server\n-d: Optional Debug/Verbose Output\n", argv[0]);
        exit(1);
    }
    
    /*should debug output be enabled?*/
    if(argc == 4) {
        if (strcmp("-d", argv[3]) == 0) {
            debug = 1;
            dprint("Debug/verbose option enabled.\n");
        }
    }
    
    blockSize = atoi(argv[1]);
    daprint("Password blocksize set to: %i.\n", blockSize);
    charBlock = (char*)malloc(blockSize * sizeof(char));
    if (charBlock == NULL) {
        /*Memory allocation failed--exit*/
        printError("Memory allocation failed while trying to reserve the blocksize!\n")
        exit(0);
    }
    
    serverCmd = (char*)calloc(strlen(serverArgs) + strlen(argv[2]) + 1, sizeof(char));
    if (serverCmd == NULL) {
        /*Memory allocation failed--exit*/
        printError("Memory allocation failed while forming the command needed to call the server!\n")
        exit(0);
    }
    strcpy(serverCmd, argv[2]);
    strcat(serverCmd, serverArgs);
    daprint("About to call server with this command: %s\n", serverCmd);
    
    checkFile = fopen("check.pw", "w+");
    if (checkFile == NULL) {
        printError("Attempt to create check.pw failed!\n");
        exit(0);
    }
    errorCode = setvbuf (checkFile, 0, _IONBF, 0);
    if (errorCode != 0) {
        printError("Attempt to set check.pw as non-buffered failed!\n");
        printError("Attempting to continue anyway...\n");
    }
    
    while (loginSuccess != 1) {
        if(currPassIndex > 10) {
            printError("EPIC FAIL! Guessed password length has exceeded 10 character block sets in length.\n")
            break;
        }
        for(currChar = 0; currChar < 4; currChar++) {
            /*write out file "check.pw"*/
            rewind(checkFile);
            for(i=0; i < currPassIndex; i++) {
                for(n=0; n < blockSize; n++) {
                    charBlock[n] = currPass[i];
                    /*if(debug) {
                        printf("%c", charBlock[n]);
                    }*/
                }
                writeSize = fwrite(charBlock, sizeof(char), blockSize, checkFile); /*ALERT: perhaps should make this a while loop or something to repeat....need to test to see how often it may fail*/
                if ((int)writeSize != blockSize) {
                    printError("Appears to be an error writing to the check.pw file. Run with -d flag to see debugging info.\n");
                    daprint("blockSize: %i\n", blockSize);
                    daprint("Return from fwrite: %i\n", writeSize);
                }
            }
            /*Current known pass is written to file--write our next letter attempt*/
            for(n=0; n < blockSize; n++) {
                charBlock[n] = charSet[currChar];
                /*if(debug) {
                    printf("%c", charBlock[n]);
                }*/
            }
            writeSize = fwrite(charBlock, sizeof(char), blockSize, checkFile); /*ALERT: perhaps should make this a while loop or something to repeat....need to test to see how often it may fail*/
            if ((int)writeSize != blockSize) {
                printError("Appears to be an error writing to the check.pw file. Run with -d flag to see debugging info.\n");
                daprint("blockSize: %i\n", blockSize);
                daprint("Return from fwrite: %i\n", writeSize);
            }
            gettimeofday(&startTime, NULL);
            /*status is 256 on fail and normal server output (e.g. logging in......0) if password is good*/
            serverStatus = system(serverCmd);
            gettimeofday(&stopTime, NULL);
            
            t1 = startTime.tv_sec+(startTime.tv_usec/1000000.0);
            t2 = stopTime.tv_sec+(stopTime.tv_usec/1000000.0);
            
            if (debug) {
                printf("New ELAPSED: %.6lf\n", t2-t1);
            }
            
            /*if (startTime.tv_usec > stopTime.tv_usec) {*/
                /*per: http://docs.hp.com/en/B2355-90682/gettimeofday.2.html*/
                /*stopTime.tv_usec += 1000000;
            }*/
            
            if(debug) {
                printf("Start Time: %ld\nStop Time: %ld\n", startTime.tv_usec, stopTime.tv_usec);
            }
            
            if (serverStatus == 0) {
                loginSuccess = 1;
                daprint("Server appears to have logged in: %i\n", serverStatus);
                break;
            }
            if (serverStatus == 256) {
                loginSuccess = 0;
                daprint("Server login failed with output: %i\n", serverStatus);
            }
            
            elapsedTime.tv_usec = stopTime.tv_usec - startTime.tv_usec;
            daprint("Current character: %c\n", charSet[currChar]);
            checkTime[currChar].tv_usec = elapsedTime.tv_usec;
            checkTime2[currChar] = t2 - t1;
            daprint("Time elapsed: %ld\n", elapsedTime.tv_usec); /*if %lu doesn't work, try %lu '%ll', '%L', '%q', %17i\n */
            
        }
        
        /*find which letter took the longest time to process--new method*/
        maxVal2 = checkTime2[0];
        maxIndex = 0;
        for(n=1; n < 4; n++) {
            if(checkTime2[n] > maxVal2) {
                maxVal2 = checkTime2[n];
                maxIndex = n;
            }
        }
        daprint("The new method's selected character is: %c\n", charSet[maxIndex]);
        
        /*find which letter took the longest time to process*/
        maxVal.tv_usec = checkTime[0].tv_usec;
        maxIndex = 0;
        for(n=1; n < 4; n++) {
            if(checkTime[n].tv_usec > maxVal.tv_usec) {
                maxVal.tv_usec = checkTime[n].tv_usec;
                maxIndex = n;
            }
        }
        currPass[currPassIndex] = charSet[maxIndex];
        daprint("The selected character is: %c\n", currPass[currPassIndex]);
        currPassIndex++;
    } /*Once the while loop is fnished or broken, currPassIndex will be one above where the last char is...it will be the length of the pw*/
    
    /* Turn off output buffering*/
    setvbuf (stdout, 0, _IONBF, 0);
    
    if(loginSuccess == 1) {
        /*Print out the password to stdout for the user to see or > */
        dprint("Apparent success! About to write out hella characters for our 'cracked' password...\n");
        for(i=0; i < currPassIndex; i++) {
            for(n=0; n < blockSize; n++) {
                printf("%c", currPass[i]);
            }
        }
    }
    
    if(debug) {
        for(i=0; i < currPassIndex; i++) {
            printf("%c", currPass[i]);
        }
        printf("\n");
    }
    
    /*Clean-up time*/
    fclose(checkFile); /*close the stream to check.pw*/
    remove("check.pw"); /*delete the "temp" file*/
    free(charBlock); /*free up some memory*/
    free(serverCmd); /*free up some more memory*/
    
    return 0;
}

